package sieve

import (
	"context"
	"errors"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize"
	"github.com/google/wire"
	"go.manyji.com/lib/config"
	"go.manyji.com/lib/log"
	"go.manyji.com/lib/util/convert"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"sieve_admin_server/app/dao"
	"sieve_admin_server/app/schema"
	schemaSieve "sieve_admin_server/app/schema/sieve"
	"sieve_admin_server/app/service/customer"
	serviceLog "sieve_admin_server/app/service/logger"
	serviceSys "sieve_admin_server/app/service/sys"
	serviceUtil "sieve_admin_server/app/service/util"
	pkgTime "sieve_admin_server/pkg/time"
	"sieve_admin_server/pkg/util"
	pkgUtil "sieve_admin_server/pkg/util"
	"strconv"
	"strings"
	"time"
)

var (
	SieveTaskSet = wire.NewSet(wire.Struct(new(SieveTaskSrv), "*"))
	sieveClient  = *config.String("sieve.user.server", "sieve_user_server", "file storage dir prefix")

	BizTyMap = map[int]string{
		1:  "ws",
		2:  "fb",
		3:  "ins",
		4:  "amazon",
		5:  "linkedin",
		6:  "viber",
		7:  "line",
		8:  "zalo",
		9:  "twitter",
		10: "skype",
		11: "tg",
		12: "momo",
		13: "mtn_momo",
		14: "microsoft",
		15: "true_money",
		16: "botim",
		17: "operator",
		18: "phone_check",
		19: "tk",
	}
)

type SieveTaskSrv struct {
	Trans                    *dao.Trans
	WarehouseRepo            *dao.WarehouseRepo
	UserRepo                 *dao.UserRepo
	InComeStatisticsSrv      *customer.InComeStatisticsSrv
	UserRecordsSrv           *customer.UserRecordsSrv
	UserSrv                  *customer.UserSrv
	RoleSrv                  *serviceSys.RoleSrv
	BusinessSrv              *serviceLog.BusinessSrv
	SieveTaskAggregationRepo *dao.SieveTaskAggregationRepo
}

func (a *SieveTaskSrv) addQueryResponseSeq(p schema.PaginationParam, req interface{}) interface{} {

	if req != nil {

		var (
			respAll = new(schemaSieve.SieveTaskAggregation)
		)
		for _, v := range req.(map[string]interface{})["list"].([]interface{}) {

			respTAs := v.(*schemaSieve.SieveTaskAggregation)
			respAll.TaskNumber += respTAs.TaskNumber
			respAll.ValidNumber += respTAs.ValidNumber
			respAll.PointsConsumeTotal += respTAs.PointsConsumeTotal
			respAll.PointsConsumeAccount += respTAs.PointsConsumeAccount
			respAll.PointsConsumeGift += respTAs.PointsConsumeGift
			respAll.AgentPoints += respTAs.AgentPoints
			respAll.Profit += respTAs.Profit
		}

		req.(map[string]interface{})["all"] = map[string]interface{}{
			"task_number":            respAll.TaskNumber,
			"valid_number":           respAll.ValidNumber,
			"points_consume_total":   respAll.PointsConsumeTotal,
			"points_consume_account": respAll.PointsConsumeAccount,
			"points_consume_gift":    respAll.PointsConsumeGift,
			"agent_points":           respAll.AgentPoints,
			"profit":                 respAll.AgentPoints,
		}
	}

	return req
}

func (a *SieveTaskSrv) Export(ctx context.Context, uid int, reqArgs map[string]interface{}, p schema.PaginationParam) interface{} {

	req := a.Query(ctx, uid, reqArgs, p)
	if req != nil {
		if err, ok := req.(error); ok {
			return err
		}
	}

	selectedItems := []string{
		"user_name",
		"task_name",
		"business_type", //
		"sieve_source_name",
		"warehouse_type", //
		"country",
		"task_number",
		"valid_number",
		"points_consume_total",
		"points_consume_account",
		"points_consume_gift",
		"agent_points",
		"platform_consume_total",
		"platform_consume_account",
		"platform_consume_gift",
		"status",
		"create_at",
		"updated_at",
		"elapsed_time",
	}
	req = a.export(ctx, req, 1, strings.Join(selectedItems, "|"))
	if req != nil {
		if _, ok := req.(error); ok {
			return req
		}
	}

	return req
}
func (a *SieveTaskSrv) export(ctx context.Context, req interface{}, funcTy int, selectedItems string) interface{} {

	var (
		xlsx       = excelize.NewFile()
		categories = map[string]string{
			"A1": "用户名",
			"B1": "任务名",
			"C1": "业务类型",
			"D1": "筛源",
			"E1": "筛料方向",
			"F1": "国家",
			"G1": "任务数",
			"H1": "有效数",
			"I1": "总积分消耗",
			"J1": "账户积分消耗",
			"K1": "赠送积分消耗",
			"L1": "代理商利润",
			"M1": "平台消耗积分",
			"N1": "平台消耗充值",
			"O1": "平台消耗赠送",
			"P1": "状态",
			"Q1": "开始时间",
			"R1": "结束时间",
			"S1": "持续时间",
		}
		selectedItemMap = make(map[string]int)
		colWidths       = map[string]float64{"A": 20, "B": 12, "C": 10, "D": 10, "E": 6, "F": 10, "G": 10, "H": 10, "I": 12, "J": 12, "K": 12, "L": 12, "M": 12, "N": 12, "O": 12, "P": 20, "R": 20, "S": 10}
	)

	for key, value := range categories {
		xlsx.SetCellValue("Sheet1", key, value)
		_style, err := xlsx.NewStyle(`{"fill":{"type":"pattern","color":["#EEECE1"],"pattern":1},"alignment":{"horizontal":"center","vertical":"center"},"font":{"bold":true,"family":"宋体","size":10}}`)
		if err == nil {
			xlsx.SetCellStyle("Sheet1", key, key, _style)
		}
	}

	xlsx.SetRowHeight("Sheet1", 1, 20)

	for k, v := range colWidths {
		xlsx.SetColWidth("Sheet1", k, k, v)
	}

	for i, v := range strings.Split(fmt.Sprintf("%v", selectedItems), "|") {
		selectedItemMap[v] = i
	}

	p1 := map[string]struct{}{
		"create_at":  {},
		"updated_at": {},
	}

	p2 := map[string]struct{}{
		"points_consume_total":     {},
		"points_consume_account":   {},
		"points_consume_gift":      {},
		"agent_points":             {},
		"profit":                   {},
		"platform_consume_total":   {},
		"platform_consume_account": {},
		"platform_consume_gift":    {},
	}

	v := req.(map[string]interface{})
	for index, vv := range v["list"].([]interface{}) {

		var (
			columIndex  = 'A'
			columOffset = 1
		)

		/*
			管理端
			    用户操作记录、筛料任务：
			    平台消耗积分: profit
			    平台消耗充值: 平台消耗积分*（用户消耗充值/用户消耗积分*100%） = profit * (points_consume_account / points_consume_total * 100%)
			    平台消耗赠送: 平台消耗积分*（用户消耗赠送/用户消耗积分*100%）=  profit * (points_consume_gift / points_consume_total * 100%)
			    代理商利润: agent_points
		*/
		kv := util.Struct2Map(vv, "json")
		kv["platform_consume_total"] = kv["profit"].(float64)
		kv["platform_consume_account"] = kv["profit"].(float64) * (kv["points_consume_account"].(float64) / kv["points_consume_total"].(float64) * 1)
		kv["platform_consume_gift"] = kv["profit"].(float64) * (kv["points_consume_gift"].(float64) / kv["points_consume_total"].(float64) * 1)
		warehouseTy, _ := strconv.Atoi(fmt.Sprintf("%v", kv["warehouse_type"]))
		for kkk, vvv := range kv {

			if len(selectedItemMap) > 0 {
				if i, ok := selectedItemMap[kkk]; !ok {
					continue
				} else {
					columOffset = i
				}
			}

			cell := fmt.Sprintf("%c", int(columIndex)+columOffset) + convert.ToString(index+2)

			if reflect.TypeOf(vvv).Kind() == reflect.Ptr {
				vOf := reflect.ValueOf(vvv)
				if vOf.IsNil() {
					vvv = ""
				} else {
					vvv = vOf.Elem()
				}
			}

			if kkk == "status" {
				statusTy, _ := strconv.Atoi(fmt.Sprintf("%v", vvv))
				vvv = map[int]string{
					1: "进行中",
					2: "暂停中",
					3: "已停止",
					4: "已完成",
				}[statusTy]
			}
			if kkk == "business_type" {

				bizTy, _ := strconv.Atoi(fmt.Sprintf("%v", vvv))
				var (
					bizName = map[int]string{
						1: "筛有效",
						2: "筛活跃",
						3: "筛性别",
					}[bizTy]
				)

				if warehouseTy == 17 {
					bizName = fmt.Sprintf("%v", map[int]string{
						1: "运营商标准筛",
						2: "运营商高级筛",
					}[bizTy])
				}

				if warehouseTy == 19 {
					bizName = fmt.Sprintf("%v", map[int]string{
						1: "筛有效",
						2: "格式筛",
					}[bizTy])
				}

				vvv = bizName
			}

			if kkk == "warehouse_type" {
				t, _ := strconv.Atoi(fmt.Sprintf("%v", vvv))
				vvv = BizTyMap[t]
			}

			if _, ok := p1[kkk]; ok {
				vvv = pkgTime.Format(vvv.(int64))
			}

			if _, ok := p2[kkk]; ok {
				vvv = pkgUtil.Decimal(float64(vvv.(float64))/10000.0, 5)
			}

			xlsx.SetCellValue("Sheet1", cell, vvv)
		}

		xlsx.SetRowHeight("Sheet1", index+2, 18)
	}

	xlsx.SetSheetName("Sheet1", "第一页")

	fileName, filePath := a.getExportPath(convert.ToString(time.Now().UnixMilli())+".csv", 1)
	err := xlsx.SaveAs(fileName)
	if err != nil {
		return nil
	}

	req = filePath

	return req
}

func (a *SieveTaskSrv) getExportPath(fileName string, bizTy int) (string, string) {

	var (
		bizTyMap = map[int]string{
			1: "sieve_tasks",
		}
	)

	relativePath := fmt.Sprintf("%v/export/%v", bizTyMap[bizTy], fileName)

	return serviceUtil.GetFilePath(
		fmt.Sprintf("%v/%v",
			serviceUtil.FileStorage(),
			relativePath),
	), relativePath
}

func (a *SieveTaskSrv) QueryOrExport(ctx context.Context, uid int, reqArgs, extraArgs map[string]interface{}, p schema.PaginationParam) interface{} {

	if extraArgs["op"] == "search" {
		return a.Query(ctx, uid, reqArgs, p)
	}

	if extraArgs["op"] == "export" {
		p = schema.PaginationParam{
			OnlyData: true,
		}
		return a.Export(ctx, uid, reqArgs, p)
	}

	return a.Query(ctx, uid, reqArgs, p)
}

func (a *SieveTaskSrv) Query(ctx context.Context, uid int, reqArgs map[string]interface{}, p schema.PaginationParam) interface{} {

	var condition = make(map[string]string)
	if _, ok := reqArgs["warehouse_name"]; ok {
		//ctx = context.WithValue(ctx,
		//	"condition", map[string]string{"warehouse_name": "LIKE"},
		//)

		condition["warehouse_name"] = "LIKE"

		reqArgs["warehouse_name"] = "'%" + reqArgs["warehouse_name"].(string) + "%'"
	}

	if _, ok := reqArgs["user_name"]; ok {
		//ctx = context.WithValue(ctx,
		//	"condition", map[string]string{"user_name": "LIKE"},
		//)

		condition["user_name"] = "LIKE"

		reqArgs["user_name"] = "'%" + reqArgs["user_name"].(string) + "%'"
	}

	if _, ok := reqArgs["task_name"]; ok {

		condition["task_name"] = "LIKE"

		reqArgs["task_name"] = "'%" + reqArgs["task_name"].(string) + "%'"
	}

	if len(condition) > 0 {
		ctx = context.WithValue(ctx,
			"condition", condition,
		)
	}

	if _, ok := reqArgs["start"]; ok {
		if _, ok := reqArgs["end"]; ok {
			start, end := reqArgs["start"].(uint64), reqArgs["end"].(uint64)
			sql := fmt.Sprintf("( (start_time >= %v && start_time <= %v) OR (end_time >= %v && end_time <= %v))", start, end, start, end)
			ctx = context.WithValue(ctx,
				"condition_customized", sql,
			)

			delete(reqArgs, "end")
		}

		delete(reqArgs, "start")
	}

	var countryIDCondition string
	if c, ok := reqArgs["country_id"]; ok {
		countryIDCondition = fmt.Sprintf("AND warehouse.country_id = %v", c)
		delete(reqArgs, "country_id")
	}

	var wHIdCondition string
	if c, ok := reqArgs["warehouse_id"]; ok {
		countryIDCondition = fmt.Sprintf("AND warehouse.id = %v", c)
		delete(reqArgs, "warehouse_id")
	}

	var (
		bizTysSQL   string
		warehousSQL string
	)
	if v, ok := reqArgs["business_type"]; ok && v != "" {
		bizTysSQL = fmt.Sprintf(" AND sieve_task.business_type IN (%v)", reqArgs["business_type"])
	}

	if v, ok := reqArgs["warehouse_type"]; ok && v != "" {
		warehousSQL = fmt.Sprintf("AND warehouse.type = %v", reqArgs["warehouse_type"])
	}

	roleId, err := a.UserSrv.GetRoleID(ctx, uint64(uid))
	if err != nil {
		return err
	}

	var leaderCondition string
	if a.RoleSrv.IsSalespersonRole(roleId) {
		leaderCondition = fmt.Sprintf(" AND (user.id = %v OR user.leader_id = %v OR user.top_leader_id = %v)", uid, uid, uid)
	}

	var (
		columnType = schemaSieve.SieveTaskAggregation{}
		joinString = []string{
			//fmt.Sprintf(" INNER JOIN user ON user.id = sieve_task_aggregation.creator %v AND user.deleted = 0", leaderCondition),
			//fmt.Sprintf(" INNER JOIN sieve_task ON sieve_task.id = sieve_task_aggregation.task_id %v AND sieve_task.deleted = 0", bizTysSQL),
			//fmt.Sprintf(" INNER JOIN warehouse ON warehouse.id = sieve_task.warehouse_id %v AND warehouse.deleted = 0 %v %v", warehousSQL, countryIDCondition, wHIdCondition),
			//

			fmt.Sprintf(" INNER JOIN user ON user.id = sieve_task_aggregation.creator %v", leaderCondition),
			fmt.Sprintf(" INNER JOIN sieve_task ON sieve_task.id = sieve_task_aggregation.task_id %v", bizTysSQL),
			fmt.Sprintf(" INNER JOIN warehouse ON warehouse.id = sieve_task.warehouse_id %v %v %v", warehousSQL, countryIDCondition, wHIdCondition),
			fmt.Sprintf(" INNER JOIN sieve_task_progress ON sieve_task_progress.task_id = sieve_task_aggregation.task_id"),
		}

		repKeys = []string{
			`sieve_task_aggregation.id as id`,
			`sieve_task_aggregation.user_name as user_name`,
			`warehouse.type as warehouse_type`,
			`sieve_task.business_type as business_type`,
			`sieve_task_aggregation.task_id as task_id`,
			`sieve_task_progress.sieve_task_id as engine_task_id`,
			`sieve_task_aggregation.warehouse_name as warehouse_name`,
			`sieve_task_aggregation.task_name as task_name`,
			`sieve_task_aggregation.task_number as task_number`,
			`sieve_task_aggregation.valid_number as valid_number`,
			`sieve_task_aggregation.country as country`,
			`sieve_task_aggregation.points_consume_total as points_consume_total`,
			`sieve_task_aggregation.points_consume_account as points_consume_account`,
			`sieve_task_aggregation.points_consume_gift as points_consume_gift`,
			`sieve_task_aggregation.agent_points as agent_points`,
			`sieve_task_aggregation.profit as profit`,
			`sieve_task_aggregation.sieve_source_name AS sieve_source_name`,
			`sieve_task_aggregation.status as status`,
			`sieve_task_aggregation.start_time as start_time`,
			`sieve_task_aggregation.end_time as end_time`,
			`sieve_task_aggregation.elapsed_time as elapsed_time`,
			`IFNULL(sieve_task_aggregation.progress,"") as progress`,
			`sieve_task_aggregation.create_at as create_at`,
			`sieve_task_aggregation.updated_at as updated_at`,
		}
	)
	delete(reqArgs, "business_type")
	delete(reqArgs, "warehouse_type")

	ctx = context.WithValue(
		context.WithValue(ctx, "join", strings.Join(joinString, " ")),
		"column_types", columnType,
	)

	req, err := a.SieveTaskAggregationRepo.Query(ctx, reqArgs, p, repKeys)
	if err != nil {
		return err
	}

	return a.addQueryResponseSeq(p, req)
}

func (a *SieveTaskSrv) DownloadRequestCheck(data *schemaSieve.SieveDownParams) interface{} {

	if data.IDs == "" {
		return errors.New("请求参数错误")
	}

	if v := data.FileType; v == 0 {
		return errors.New("请求参数错误")
	} else {
		if v != 1 && v != 2 && v != 3 {
			return errors.New("请求参数错误")
		}
	}

	if v := data.ExportFormat; v == 0 {
		return errors.New("请求参数错误")
	} else {
		if v != 1 && v != 2 && v != 3 {
			return errors.New("请求参数错误")
		}
	}

	// 正则为：匹配数字范围为0到99之间、数字1小于数字2、格式为数字1-数字2或单纯数字
	if v := data.AgeRange; v != "" {
		re := regexp.MustCompile(`^([1-9]|[1-9]\d|0\d)-([1-9]|[1-9]\d|0\d)$|^([1-9]|[1-9]\d)$`)

		if matched := re.MatchString(v); !matched {
			return errors.New("年龄范围格式错误(范围：[0-99])")
		}
	}

	return nil
}

// 下载筛料文件时，需要从客户端的路径下载
func (a *SieveTaskSrv) path(p string) string {

	execName := strings.Split(filepath.Base(os.Args[0]), ".")[0]
	p = strings.Replace(p, execName, sieveClient, -1)

	return p
}

//func (a *SieveTaskSrv) Download(ctx context.Context, data *schemaSieve.SieveDownParams) interface{} {
//
//	if err := a.DownloadRequestCheck(data); err != nil {
//		return err
//	}
//
//	/*
//		1. len(ids) == 1 导出单条记录 && export_mode = 1 的时候,导出文件格式是.zip 并且 根据 number_of_single 数值决定单文件的号码个数
//		2. batched == 1 那么导出的也是 .zip 格式，不管 len(ids) 是多少个
//		3. 导出的文件格式 由 export_format 决定
//	*/
//	var (
//		file           string
//		batched        bool
//		exportFormat   = data.ExportFormat
//		fileType       = data.FileType
//		exportMode     = data.ExportMode
//		numberOfSingle uint64
//	)
//
//	if data.Batched == 1 {
//		batched = true
//	}
//
//	files, err := a.getDownloadFiles(ctx, data)
//	if len(files) == 0 {
//		return errors.New("导出/下载文件为空")
//	}
//
//	if err != nil {
//		log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
//		return errors.New("下载异常")
//	}
//
//	file = files[0]
//
//	// 1. 批量导出(选中多条记录导出数据,会写入到.zip文件中)
//	if batched {
//
//		ff := func(f string) string {
//
//			abF, _ := a.GetFilePath(f, data.Platform, data.BizTy)
//			return abF
//		}
//
//		zipFileName := fmt.Sprintf("%v.zip", time.Now().UnixMilli())
//
//		zipFilePath, relativePath := a.GetDownloadZipPath(zipFileName, int(data.WarehouseType))
//		if err := serviceUtil.CreateZipFile(zipFilePath, files, ff, nil); err != nil {
//
//			log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
//			return errors.New("下载异常")
//		}
//
//		return relativePath
//	}
//
//	absPath, relativePath := a.GetFilePath(file, data.Platform, fileType)
//
//	//absPath = a.path(absPath)
//	// 2. 分批导出(选中一条记录,要求将数据分多个文件导出)
//	if exportMode == 1 || exportMode == 2 {
//		var (
//			isSieveSex bool
//			fs         []string
//		)
//
//		if data.Operator != "" {
//
//			isSieveSex = data.Operator == "gender" || data.Operator == "lineGender" || data.Operator == "zaloGender"
//		}
//
//		if data.NumberOfSingle != 0 {
//			numberOfSingle = uint64(data.NumberOfSingle)
//		}
//
//		// 如果是 全部导出，那么 单文件数量直接置为0
//		if exportMode == 2 {
//			numberOfSingle = 0
//		}
//
//		if !isSieveSex {
//			if exportFormat == 1 {
//				fs, err = serviceUtil.TxtFileSplitToTxt(absPath, int(numberOfSingle))
//				if err != nil {
//					log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
//					return errors.New("下载异常")
//				}
//			}
//			if exportFormat == 2 {
//				fs, err = serviceUtil.TxtFileSplitToCSV(absPath, int(numberOfSingle))
//				if err != nil {
//					log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
//					return errors.New("下载异常")
//				}
//			}
//		} else {
//			var (
//				exportKind int
//				ageRange   string
//			)
//			if data.ExportKind != 0 {
//				exportKind = data.ExportKind
//			}
//			if data.AgeRange != "" {
//				ageRange = data.AgeRange
//			}
//			if fileType == 2 {
//				if exportFormat == 2 {
//					fs, err = serviceUtil.ExcelFileSplitToCSV(absPath, int(numberOfSingle), exportKind, ageRange)
//					if err != nil {
//						log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
//						return errors.New("下载异常")
//					}
//				}
//				if exportFormat == 3 {
//					fs, err = serviceUtil.ExcelFileSplitToHtml(absPath, int(numberOfSingle), exportKind, tableHtml, ageRange)
//					if err != nil {
//						log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
//						return errors.New("下载异常")
//					}
//				}
//			}
//			if fileType == 1 || fileType == 3 {
//				if exportFormat == 1 {
//					fs, err = serviceUtil.TxtFileSplitToTxt(absPath, int(numberOfSingle))
//					if err != nil {
//						log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
//						return errors.New("下载异常")
//					}
//				}
//				if exportFormat == 2 {
//					fs, err = serviceUtil.TxtFileSplitToCSV(absPath, int(numberOfSingle))
//					if err != nil {
//						log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
//						return errors.New("下载异常")
//					}
//				}
//			}
//		}
//
//		if len(fs) <= 0 {
//			return errors.New("导出/下载文件为空")
//		}
//
//		// 表示不分割文件，就是文件格式转换
//		if numberOfSingle == 0 {
//			// 重新生成文件名，不跟 分割的文件名同名
//			ss := strings.Split(filepath.Base(fs[0]), ".")
//			fNew := filepath.Join(filepath.Dir(fs[0]), convert.ToString(time.Now().UnixNano())+"."+ss[len(ss)-1])
//
//			if err = os.Rename(fs[0], fNew); err != nil {
//				return err
//			}
//			absPath, relativePath = a.GetFilePath(fNew, data.Platform, data.FileType)
//		} else {
//			zipFileName := fmt.Sprintf("%v.zip", time.Now().Unix())
//			zipFilePath, rp := a.GetDownloadZipPath(zipFileName, int(data.WarehouseType))
//			if err := serviceUtil.CreateZipFile(zipFilePath, fs, func(f string) string { return f }, nil); err != nil {
//				return err
//			}
//			relativePath = rp
//		}
//
//		go func() {
//			time.Sleep(time.Hour)
//			for _, f := range fs {
//				_ = os.Remove(f)
//			}
//		}()
//	}
//
//	return relativePath
//}

func (a *SieveTaskSrv) isDownXlsx(operator string) bool {

	return operator == "gender" || operator == "lineDown" || operator == "lineGender" ||
		operator == "zaloGender" || operator == "operatorStandard" || operator == "tgActiveDown" ||
		operator == "operatorPremium" || operator == "viberActiveDown" || operator == "viberDown" ||
		operator == "linkedinSeniorDown" || operator == "linkedinGenderDown" || operator == "excelDown" || operator == "excelCSV"
}

func (a *SieveTaskSrv) Download(ctx context.Context, data *schemaSieve.SieveDownParams) interface{} {

	if err := a.DownloadRequestCheck(data); err != nil {
		return err
	}

	/*
		1. len(ids) == 1 导出单条记录 && export_mode = 1 的时候,导出文件格式是.zip 并且 根据 number_of_single 数值决定单文件的号码个数
		2. batched == 1 那么导出的也是 .zip 格式，不管 len(ids) 是多少个
		3. 导出的文件格式 由 export_format 决定
	*/

	/*
		ids: sieve_task_aggregation 表的主键ID，多个 用 "|" 拼接
		file_type: 1:原文件,2:筛后原文件,3:排重原文件
		batched: 是否批量导出
		export_format: 导出格式(1:txt,2:csv,3:html)
		export_type: 导出类型(1:ws群发格式,2:短信格式)
		export_kind: 导出类型(1:男性数据,2:女性数据,3:开通数据,4:所有性别) 只有性别筛才有这个参数
		age_range: export_kind = (1,2,4) 的时候,这个参数有效(格式为10-20,或者25)
		export_mode: 导出方式(1:分批导出,2:全部导出)
		number_of_single: 单文件数量（最小100，最大100w）
	*/
	var (
		file             string
		batched          bool
		exportFormat     = data.ExportFormat
		batchedFileCount = data.BatchedFileCount
		fileType         = data.FileType
		exportMode       = data.ExportMode
		warehouseTy      = data.WarehouseType
		numberOfSingle   uint64
		fNameCallBack    = func(userName string, bizTy, warehouseTy int, warehouseName string, country string) string {

			var (
				bizName = map[int]string{
					1: "筛有效",
					2: "筛活跃",
					3: "筛性别",
				}[bizTy]
			)

			if warehouseTy == 17 {
				bizName = fmt.Sprintf("%v", map[int]string{
					1: "运营商标准筛",
					2: "运营商高级筛",
				}[bizTy])
			}

			// 用户名去掉特殊字符
			for _, char := range []string{"/", "\\", ":", "*", "?", "\"", "<", ">", "|"} {
				userName = strings.Replace(userName, char, "", -1)
			}

			// 仓库名去掉特殊字符
			for _, char := range []string{"/", "\\", ":", "*", "?", "\"", "<", ">", "|"} {
				warehouseName = strings.Replace(warehouseName, char, "", -1)
			}

			t := time.Now()
			date := pkgTime.Format(t.Unix(), "20060102150405")
			return fmt.Sprintf("%v_%v%v_%v_%v_%v_%v", userName, BizTyMap[warehouseTy], bizName, warehouseName, country, date, t.Nanosecond())
		}
	)

	if data.Batched == 1 {
		batched = true
	}

	files, fName, fileMap, err := a.getDownloadFiles(ctx, data, fNameCallBack)
	if len(files) == 0 {
		return errors.New("导出/下载文件为空")
	}

	if err != nil {
		log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
		return errors.New("下载异常")
	}

	file = files[0]

	// 1. 批量导出(选中多条记录导出数据,会写入到.zip文件中)
	if batched {

		fCallBack := func(f string) string {

			// 批量导出的，前端无法传给warehouse_type，这里从数据库获取
			platform := BizTyMap[convert.Atoi(strings.Split(fileMap[f], "_")[0])]
			abF, _ := a.GetFilePath(f, platform, fileType)
			return abF
		}

		fileExt := filepath.Ext(file)
		if batchedFileCount == 1 && fileExt == ".txt" {
			mergeFileName := fmt.Sprintf("%v.txt", time.Now().UnixMilli())
			mergeFilePath, _ := a.GetFilePath(mergeFileName, data.Platform, fileType)
			ff := fCallBack
			if err := serviceUtil.MergeFiles(mergeFilePath, files, ff); err != nil {
				//files = []string{mergeFilePath}
				log.Err(fmt.Sprintf("合并文件错误:%+v", err))
			}

			files = []string{mergeFilePath}
			fCallBack = func(f string) string { return f }
		}

		zipFileName := fmt.Sprintf("%v.zip", time.Now().UnixMilli())

		zipFilePath, relativePath := a.GetDownloadZipPath(zipFileName, int(data.WarehouseType))

		if err := serviceUtil.CreateZipFile(zipFilePath, files, fCallBack, nil); err != nil {

			log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
			return errors.New("下载异常")
		}

		return relativePath
	}

	data.Platform = BizTyMap[convert.Atoi(strings.Split(fileMap[file], "_")[0])]
	absPath, relativePath := a.GetFilePath(file, data.Platform, fileType)

	// 2. 分批导出(选中一条记录,要求将数据分多个文件导出)
	if exportMode == 1 || exportMode == 2 {
		var (
			isXlsx bool
			fs     []string
		)

		if data.DownLoadOperator != "" {

			//isXlsx = data.Operator == "gender" || data.Operator == "lineGender" || data.Operator == "zaloGender" || data.Operator == "operatorStandard" || data.Operator == "operatorPremium"
			isXlsx = a.isDownXlsx(data.DownLoadOperator)
		}

		if data.NumberOfSingle != 0 {
			numberOfSingle = uint64(data.NumberOfSingle)
		}

		// 如果是 全部导出，那么 单文件数量直接置为0
		if exportMode == 2 {
			numberOfSingle = 0
		}

		if !isXlsx {

			if exportFormat == 1 {
				fs, err = serviceUtil.TxtFileSplitToTxt(absPath, int(numberOfSingle))
				if err != nil {
					log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
					return errors.New("下载异常")
				}
			}

			if exportFormat == 2 {
				fs, err = serviceUtil.TxtFileSplitToCSV(absPath, int(numberOfSingle))
				if err != nil {
					log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
					return errors.New("下载异常")
				}
			}
		} else {
			var (
				exportKind int
				ageRange   string
			)
			if data.ExportKind != 0 {
				exportKind = data.ExportKind
			}
			if data.AgeRange != "" {
				ageRange = data.AgeRange
			}
			if fileType == 2 {
				// 运营商导出支持 txt格式 和 xlsx格式
				if warehouseTy == 17 {
					fCallBackMap := map[string]func(fileName string, npf int, args ...interface{}) ([]string, error){
						".xlsx_1": serviceUtil.OperatorExcelFileSplitToTxt,
						".xls_1":  serviceUtil.OperatorExcelFileSplitToTxt,
						".xls_2":  serviceUtil.OperatorExcelFileSplitToExcel,
						".xlsx_2": serviceUtil.OperatorExcelFileSplitToExcel,
						".csv_1":  serviceUtil.CSVFileSplitToTxt,
						".csv_2":  serviceUtil.CSVFileSplitToCSV,
					}

					key := fmt.Sprintf("%v_%v", filepath.Ext(file), exportFormat)
					fCallBack, ok := fCallBackMap[key]
					if !ok {
						return errors.New("不支持的下载方式")
					}

					fs, err = fCallBack(absPath, int(numberOfSingle), exportKind, ageRange, tableHtml, true)
					if err != nil {
						log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
						return errors.New("下载异常")
					}
				} else {
					fCallBackMap := map[string]func(fileName string, npf int, args ...interface{}) ([]string, error){
						".xlsx_1": serviceUtil.ExcelFileSplitToTxt,
						".xls_1":  serviceUtil.ExcelFileSplitToTxt,
						".xls_2":  serviceUtil.SexExcelFileSplitToExcel,
						".xlsx_2": serviceUtil.SexExcelFileSplitToExcel,
						".xlsx_3": serviceUtil.SexExcelFileSplitToHtml,
						".xls_3":  serviceUtil.SexExcelFileSplitToHtml,
						".csv_1":  serviceUtil.CSVFileSplitToTxt,
						".csv_2":  serviceUtil.CSVFileSplitToCSV,
						".csv_3":  serviceUtil.SexCSVFileSplitToHtml,
						".txt_1":  serviceUtil.TxtFileSplitToTxt,
						".txt_2":  serviceUtil.TxtFileSplitToCSV,
					}

					key := fmt.Sprintf("%v_%v", filepath.Ext(file), exportFormat)
					fCallBack, ok := fCallBackMap[key]
					if !ok {
						return errors.New("不支持的下载方式")
					}

					fs, err = fCallBack(absPath, int(numberOfSingle), exportKind, ageRange, tableHtml, false)
					if err != nil {
						log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
						return errors.New("下载异常")
					}
				}
			}

			if fileType == 1 || fileType == 3 {
				if exportFormat == 1 {
					fs, err = serviceUtil.TxtFileSplitToTxt(absPath, int(numberOfSingle))
					if err != nil {
						log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
						return errors.New("下载异常")
					}
				}
				if exportFormat == 2 {
					fs, err = serviceUtil.TxtFileSplitToCSV(absPath, int(numberOfSingle))
					if err != nil {
						log.Err(fmt.Sprintf("请求参数：%+v, 下载异常：%+v", *data, err))
						return errors.New("下载异常")
					}
				}
			}
		}

		if len(fs) <= 0 {
			return errors.New("导出/下载文件为空")
		}

		// 表示不分割文件，就是文件格式转换
		if numberOfSingle == 0 {
			// 重新生成文件名，不跟 分割的文件名同名
			ss := strings.Split(filepath.Base(fs[0]), ".")
			fNew := filepath.Join(filepath.Dir(fs[0]), fName+"."+ss[len(ss)-1])

			if err = os.Rename(fs[0], fNew); err != nil {
				return err
			}

			absPath, relativePath = a.GetFilePath(fNew, data.Platform, data.FileType)
		} else {
			zipFileName := fmt.Sprintf("%v.zip", time.Now().Unix())
			zipFilePath, rp := a.GetDownloadZipPath(zipFileName, int(data.WarehouseType))
			if err := serviceUtil.CreateZipFile(zipFilePath, fs, func(f string) string { return f }, nil); err != nil {
				return err
			}
			relativePath = rp
		}

		go func() {
			time.Sleep(time.Hour)
			for _, f := range fs {
				_ = os.Remove(f)
			}
		}()
	}

	return relativePath
}

func (a *SieveTaskSrv) GetDownloadZipPath(fileName string, bizTy int, args ...interface{}) (string, string) {

	var (
		sieveUserBase = *config.String("sieve_user_server.exec_name", "sieve_user_server", "file storage dir prefix")
		sieveUserDir  = *config.String("sieve_user_server.exec_path", "/data/sieve_user_server", "file storage dir prefix")
		bizTyMap      = map[int]string{
			1:  "ws",
			2:  "fb",
			3:  "ins",
			4:  "amazon",
			5:  "linkedin",
			6:  "viber",
			7:  "line",
			8:  "zalo",
			9:  "twitter",
			10: "skype",
			11: "tg",
			12: "momo",
			13: "mtn_momo",
			14: "microsoft",
			15: "true_money",
			16: "botim",
			17: "operator",
			18: "phone_check",
		}
	)
	relativePath := fmt.Sprintf("%v/zip/%v", bizTyMap[bizTy], fileName)

	return serviceUtil.GetFilePath(
		fmt.Sprintf("%v/%v",
			serviceUtil.FileStorage(sieveUserBase),
			relativePath), false, sieveUserDir,
	), relativePath
}

func (a *SieveTaskSrv) GetFilePath(fileName string, platform string, funcTy int, args ...interface{}) (string, string) {

	var (
		sieveUserBase = *config.String("sieve_user_server.exec_name", "sieve_user_server", "file storage dir prefix")
		sieveUserDir  = *config.String("sieve_user_server.exec_path", "/data/sieve_user_server", "file storage dir prefix")
		pathPrefix    = map[int]string{
			1: "upload",
			2: "sieve",
			3: "dedupe",
			4: "ng",  // number_generation
			5: "cur", // current_warehouse
		}
		bitArray    bool
		bitArrayMap = map[bool]string{
			true: "bitarray_",
		}
	)
	if len(args) == 1 {
		bitArray = args[0].(bool)
	}

	relativePath := fmt.Sprintf("%v/%v/%v%v", platform, pathPrefix[funcTy], bitArrayMap[bitArray], strings.TrimLeft(filepath.Base(fileName), bitArrayMap[bitArray]))

	return serviceUtil.GetFilePath(
		fmt.Sprintf("%v/%v",
			serviceUtil.FileStorage(sieveUserBase),
			relativePath), false, sieveUserDir,
	), relativePath
}

func (a *SieveTaskSrv) getDownloadFiles(ctx context.Context, data *schemaSieve.SieveDownParams, fNameCallBack func(userName string, bizTy, warehouseTy int, warehouseName string, country string) string) ([]string, string, map[string]string, error) {

	type columnType struct {
		Id                int64  `json:"id" db:"id"`
		UserName          string `json:"user_name" db:"user_name"`         // 用户名称（上传料子的用户名称）
		BusinessType      int    `json:"business_type" db:"business_type"` // 表示业务类型（1：筛有效，2：筛活跃，3：筛性别）
		WarehouseType     int    `json:"warehouse_type" db:"warehouse_type"`
		WarehouseName     string `json:"warehouse_name" db:"warehouse_name"` // 仓库名称（创建的仓库名称）
		Country           string `json:"country" db:"country"`               // 国家（料子的所属国家）
		File              string `json:"file" db:"file"`
		SieveFile         string `json:"sieve_file" db:"sieve_file"`                   //  筛后的文件路径
		DeDuplicationFile string `json:"de_duplication_file" db:"de_duplication_file"` //  排重后的文件路径
	}

	var (
		querySQL = fmt.Sprintf(`
SELECT
  sieve_task_aggregation.id AS id,
  sieve_task_aggregation.user_name AS user_name,
  sieve_task.business_type AS business_type,
  warehouse.type AS warehouse_type,
  sieve_task_aggregation.warehouse_name AS warehouse_name,
  sieve_task_aggregation.country AS country,
  sieve_task.file AS file,
  sieve_task_aggregation.sieve_file AS sieve_file,
  sieve_task_aggregation.de_duplication_file AS de_duplication_file
FROM sieve_task_aggregation
  INNER JOIN sieve_task
    ON sieve_task.id = sieve_task_aggregation.task_id
    AND sieve_task.deleted = 0
  INNER JOIN warehouse ON warehouse.id = sieve_task.warehouse_id AND warehouse.deleted = 0
WHERE sieve_task_aggregation.deleted = 0
AND sieve_task_aggregation.id IN (%v)
ORDER BY sieve_task_aggregation.create_at DESC
`, strings.Join(strings.Split(data.IDs, "|"), ","))
	)

	resp, err := a.SieveTaskAggregationRepo.QueryRaw(ctx, querySQL, columnType{})
	if err != nil {
		return nil, "", nil, err
	}

	var (
		fileTy  = data.FileType
		files   []string
		fName   string
		fileMap = map[string]string{}
	)
	for _, v := range resp {

		var f string
		switch fileTy {
		case 1:
			f = v.(*columnType).File
		case 2:
			f = v.(*columnType).SieveFile
		case 3:
			f = v.(*columnType).DeDuplicationFile
		}

		if f == "" {
			continue
		}
		files = append(files, f)
		fileMap[f] = fmt.Sprintf("%v_%v", v.(*columnType).WarehouseType, v.(*columnType).BusinessType)
		if fNameCallBack != nil {
			fName = fNameCallBack(v.(*columnType).UserName, v.(*columnType).BusinessType, v.(*columnType).WarehouseType, v.(*columnType).WarehouseName, v.(*columnType).Country)
		}
	}

	if len(files) == 0 {
		return nil, "", nil, errors.New("empty files")
	}
	return files, fName, fileMap, nil
}
